Serverless FrameworkとAWS SAMからDynamoDBのTTL機能を有効化する
はじめに
こんにちは、中山です。
少し前の話ですがDynamoDBにTTL機能が追加されました。属性の作成日時をベースに特定期間経過した場合、自動的にアイテムを削除してくれる機能です。DynamoDBを一時的なデータの保管場所として利用しているユースケースでは、嬉しいアップデートだったのではないでしょうか。以前までは、バッチ処理などでアイテムの削除を実施するアプリを自分で作り込む必要がありました。が、この機能を利用することによりAWSにまるっとその部分をおまかせできるという訳です。より詳しい内容は以下のリンクを参照ください。
DynamoDBはサーバレスアーキテクチャのデータストアとしてよく利用されます。私はServerless FrameworkやAWS SAM(AWS Serverless Application Model)をよく利用しているので、それらのツールから使いたかったのですが、執筆時点(2017/04/29)ではCloudFormationのAWS::DynamoDB::TableおよびAWS::Serverless::SimpleTableリソースがTTL用プロパティをサポートしていません。そのため、少し工夫が必要になります。
という訳で、本エントリではこれらのツールからDynamoDBのTTL機能を利用する方法をご紹介します。
Serverless Frameworkの場合
以下のプラグインを利用することで対応可能です。
使い方は簡単です。プラグインのインストール後、 serverlelss.yml
を修正するだけです。
- プラグインのインストール
$ npm install --save serverless-dynamodb-ttl
serverless.yml
の修正
# プラグインの読み込み plugins: - serverless-dynamodb-ttl # TTLの設定 custom: dynamodb: ttl: - table: your-dynamodb-table-name field: your-ttl-property-name
例えば、以下のように serverless.yml
を定義したとします。
service: dynamodb-ttl custom: config: ${file(config.yml)} dynamodb: ttl: - table: ${self:custom.config.tableName} field: ttl frameworkVersion: ">=1.12.1 <2.0.0" provider: name: aws runtime: nodejs6.10 stage: ${self:custom.config.stage} region: ${self:custom.config.region} cfLogs: true iamRoleStatements: - Sid: DynamoDBAccess Effect: Allow Resource: - Fn::Join: [ "", [ "arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":", "table/", Ref: TestTable ] ] Action: - dynamodb:* plugins: - serverless-dynamodb-ttl package: include: - src/** exclude: - .git/** - config.yml - package.json - serverless.yml - node_modules/** functions: scan: handler: src/handlers/dynamodb/index.scan environment: TableName: Ref: TestTable put: handler: src/handlers/dynamodb/index.put environment: TableName: Ref: TestTable resources: Resources: TestTable: Type: AWS::DynamoDB::Table Properties: TableName: ${self:custom.config.tableName} AttributeDefinitions: - AttributeName: ttl AttributeType: N - AttributeName: time AttributeType: S KeySchema: - AttributeName: ttl KeyType: HASH - AttributeName: time KeyType: RANGE ProvisionedThroughput: ReadCapacityUnits: 1 WriteCapacityUnits: 1
デプロイ後にHookがバインドされているので sls deploy
の最後に、以下のような出力が表示されTTLが有効化されます。
$ sls deploy -v -r ap-northeast-1 <snip> Serverless: Enabling TTL setting(s) for DynamoDB
DynamoDBのテーブルを確認すると、意図した動作がされていることを確認できます。
$ aws dynamodb describe-time-to-live \ --table-name testTable { "TimeToLiveDescription": { "TimeToLiveStatus": "ENABLED", "AttributeName": "ttl" } }
注意点があります。このプラグインでは、現状(v0.0.6)DynamoDBオブジェクトを作成する際のリージョン指定がオプションから渡されるため、 -r <region>
でCLIから明示的にリージョンを指定する必要があります。この指定をしないと、以下のようなエラーが表示されます。
Serverless: Enabling TTL setting(s) for DynamoDB Config Error ------------------------------------------- Missing region in config
AWS SAMの場合
AWS SAMはプラグイン機構がないので、自分で作り込む必要があります。とはいえ、大した話ではないです。実装方法はいくつか考えられますが、一番シンプルな方法はLambda-backed Custom ResourceでUpdateTimeToLive APIを叩く方法だと思います。実装方法はLambdaがまだDead Letter Queue用プロパティをサポートしていない際にご紹介した方法とほぼ同じです。
メインとなるテンプレートを以下のように定義しておきます。
--- AWSTemplateFormatVersion: 2010-09-09 Transform: AWS::Serverless-2016-10-31 Description: DynamoDB TTL Main Stack Resources: DynamoDB: Type: AWS::CloudFormation::Stack Properties: TemplateURL: src/templates/dynamodb.yml Scan: Type: AWS::Serverless::Function Properties: CodeUri: src/handlers/dynamodb Handler: index.scan Runtime: nodejs6.10 Policies: - Version: 2012-10-17 Statement: - Sid: DynamoDBAccess Effect: Allow Action: - dynamodb:* Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${DynamoDB.Outputs.TableName} Environment: Variables: TableName: !GetAtt DynamoDB.Outputs.TableName Put: Type: AWS::Serverless::Function Properties: CodeUri: src/handlers/dynamodb Handler: index.put Runtime: nodejs6.10 Environment: Variables: TableName: !GetAtt DynamoDB.Outputs.TableName Policies: - Version: 2012-10-17 Statement: - Sid: DynamoDBAccess Effect: Allow Action: - dynamodb:* Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${DynamoDB.Outputs.TableName} Custom: Type: AWS::CloudFormation::Stack Properties: TemplateURL: src/templates/custom.yml Parameters: TableName: !GetAtt DynamoDB.Outputs.TableName AttributeName: ttl Outputs: TableName: Value: !GetAtt DynamoDB.Outputs.TableName Scan: Value: !Ref Scan Put: Value: !Ref Put
Lambda-backed Custom Resourceを利用するテンプレートを以下のように定義します。ちゃんとエラー処理を実装していない点は生暖かく見守ってください。
--- AWSTemplateFormatVersion: 2010-09-09 Description: DynamoDB TTL Custom Stack Parameters: TableName: Type: String AttributeName: Type: String Resources: LambdaRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Sid: AssumeRolePolicy Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: !Sub ${AWS::StackName}-Policy PolicyDocument: Version: 2012-10-17 Statement: - Sid: DynamoDBAccess Effect: Allow Action: - dynamodb:UpdateTimeToLive Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${TableName} LambdaUpdateTimeToLive: Type: AWS::Lambda::Function Properties: Handler: index.handler Role: !GetAtt LambdaRole.Arn Runtime: nodejs6.10 Code: ZipFile: | const response = require('cfn-response'); const AWS = require('aws-sdk'); exports.handler = (event, context, callback) => { let responseData = {}; if (event.RequestType === 'Delete') { response.send(event, context, response.SUCCESS, responseData); } const dynamodb = new AWS.DynamoDB(); let params = { TableName: event.ResourceProperties.TableName, TimeToLiveSpecification: { AttributeName: event.ResourceProperties.AttributeName, Enabled: true } }; dynamodb.updateTimeToLive(params, (err, data) => { if (err) { console.log(err, err.stack); response.send(event, context, response.FAILED, responseData); } else { response.send(event, context, response.SUCCESS, responseData); } }); }; CustomUpdateTimeToLive: Type: Custom::UpdateTimeToLive Version: 1.0 Properties: ServiceToken: !GetAtt LambdaUpdateTimeToLive.Arn TableName: !Ref TableName AttributeName: !Ref AttributeName
いつも通り aws cloudformation package
と aws cloudformation deploy
実施後、DynamoDBのテーブルを確認すると、以下のように意図した結果になっていることが確認できます。
$ aws dynamodb describe-time-to-live \ --table-name dynamodb-ttl-DynamoDB-13DRL8A11KJYA-TestTable-1LP3MV3HIR1U8{ "TimeToLiveDescription": { "TimeToLiveStatus": "ENABLED", "AttributeName": "ttl" } }
まとめ
いかがだったでしょうか。
Serverless FrameworkとAWS SAMからDynamoDBのTTL機能を利用する方法をご紹介しました。2つのツールはかなり似ていますが、できること/できないことが微妙に異なります。今後も、これらのツールについてご紹介していきたいと思います。
本エントリがみなさんの参考になれば幸いに思います。